/*
 * Copyright (C) 2004-2007  Autodesk, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of version 2.1 of the GNU Lesser
 * General Public License as published by the Free Software Foundation.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "stdafx.h"
#include "FdoCMSSelectCommand.h"
#include "FdoCMSFeatureReader.h"
#include "FdoCMSUtils.h"
#include "FdoCMSGlobals.h"
#include "FdoCMSXmlGlobals.h"
#include <CMS/Override/FdoCMSOvPhysicalSchemaMapping.h>
#include "FdoCMSDelegateFactory.h"
#include <malloc.h>
#include <math.h>

#define MAXPOWER2RESOLUTION 4096

FdoCMSSelectCommand::FdoCMSSelectCommand (FdoCMSConnection* connection) :
    FdoCMSFeatureCommand<FdoISelect> (connection),
    mType (FdoLockType_None),
    mStrategy (FdoLockStrategy_All)
{
    mPropertiesToSelect = FdoIdentifierCollection::Create ();
}

/** Do not implement the copy constructor. **/
//FdoCMSSelectCommand::FdoCMSSelectCommand (const FdoCMSSelectCommand &right) { }

FdoCMSSelectCommand::~FdoCMSSelectCommand (void)
{
}

// <summary>Dispose this object.</summary>
// <returns>Returns nothing.</returns> 
void FdoCMSSelectCommand::Dispose ()
{
    delete this;
}

/// <summary>Gets the FdoIdentifierCollection that holds the list of property names to 
/// return with the result. If empty all properties of the specified class
/// are returned.</summary>
/// <returns>Returns the list of property names.</returns> 
FdoIdentifierCollection* FdoCMSSelectCommand::GetPropertyNames ()
{
    if (mPropertiesToSelect == NULL)
        mPropertiesToSelect = FdoIdentifierCollection::Create();

    return (FDO_SAFE_ADDREF(mPropertiesToSelect.p));
}

/// <summary>Gets the FdoLockType value (see "Locking Commands").</summary>
/// <returns>Returns the lock type.</returns> 
FdoLockType FdoCMSSelectCommand::GetLockType ()
{
    return (mType);
}

/// <summary>Sets the FdoLockType value (see "Locking Commands").</summary>
/// <param name="value">Input the lock type.</param> 
/// <returns>Returns nothing</returns> 
void FdoCMSSelectCommand::SetLockType (FdoLockType value)
{
    throw FdoSchemaException::Create(NlsMsgGet(FDOCMS_LOCKING_NOT_SUPPORTED, "Locking not supported."));
}

/// <summary>Gets the FdoLockStrategy value (see "Locking Commands").</summary>
/// <returns>Returns the lock strategy.</returns> 
FdoLockStrategy FdoCMSSelectCommand::GetLockStrategy ()
{
    return (mStrategy);
}

/// <summary>Sets the FdoLockStrategy value (see "Locking Commands").</summary>
/// <param name="value">Input the lock strategy.</param> 
/// <returns>Returns nothing</returns> 
void FdoCMSSelectCommand::SetLockStrategy (FdoLockStrategy value)
{
    throw FdoSchemaException::Create(NlsMsgGet(FDOCMS_LOCKING_NOT_SUPPORTED, "Locking not supported."));
}

/// <summary>Executes the select command and returns a reference to an FdoIFeatureReader.</summary>
/// <returns>Returns the feature reader.</returns> 
FdoIFeatureReader* FdoCMSSelectCommand::Execute ()
{
    VALIDATE_ARGUMENT (mClassName);

    FdoCMSDelegateP CMSDelegate = _getCMSDelegate ();

    FdoStringP schemaName = mClassName->GetSchemaName ();
    FdoStringP clsName = mClassName->GetName ();	
    VALIDATE_ARGUMENT (clsName);

    // Get the class in the schemas
    FdoFeatureClassP featClass;
    FdoFeatureSchemasP schemas = mConnection->GetSchemas ();
    if (schemaName != NULL && schemaName.GetLength() > 0)
    {
        FdoFeatureSchemaP schema = schemas->GetItem (schemaName);
        if (schema == NULL)
            throw FdoSchemaException::Create (NlsMsgGet (FDOCMS_NAMED_SCHEMA_NOT_FOUND, "FDO Schema '%1$ls' was not found.", (FdoString*)schemaName));

        FdoPtr<FdoClassCollection> featClasses = schema->GetClasses ();
        featClass = static_cast<FdoFeatureClass *> (featClasses->GetItem (clsName));
    }
    else
    {
        FdoIDisposableCollection* featClasses = schemas->FindClass (clsName);	
        if (featClasses->GetCount () == 0)
            throw FdoSchemaException::Create (NlsMsgGet(FDOCMS_NAMED_SCHEMACLASS_NOT_FOUND, "FDO Feature Class '%1$ls' was not found.", (FdoString*)clsName ));

        if (featClasses->GetCount () > 1)
            throw FdoSchemaException::Create (NlsMsgGet(FDOCMS_NAMED_SCHEMACLASS_NOT_FOUND, "Duplicate FDO Feature Class '%1$ls' found in Schema.", (FdoString*)clsName ));

        featClass = static_cast<FdoFeatureClass *> (featClasses->GetItem (0));
    }

    if (featClass == NULL)
        throw FdoSchemaException::Create (NlsMsgGet(FDOCMS_NAMED_SCHEMACLASS_NOT_FOUND, "FDO Feature Class '%1$ls' was not found.", (FdoString*)clsName));

    // Check whether the feat class is abstract or not.
    if (featClass->GetIsAbstract ())
        throw FdoSchemaException::Create (NlsMsgGet(FDOCMS_CANNOT_QUERY_ABSTRACT_CLASS, "FDO Query cann't be performed on abstract class '%1$ls'.", (FdoString*)clsName ));

    // TODO assert
    FdoPtr<FdoCMSBoundingBox> bbox = FdoCMSBoundingBox::Create ();

    FdoStringP srsName = mConnection->GetActiveSpatialContext ();
    //bbox->SetCRS (srsName);
    
    // HEIGHT and WIDTH paramater in the GetMap request
    FdoSize height = 0;
    FdoSize width = 0; 

    //if (!mConnection->IsConfigured ())
    //{
    //	imageFormat = mConnection->GetDefaultImageFormat ();
    //}

    // TODO: Calculate the bounding box of the layer(s)
    // _calcLayersBoundingBox (selectedLayers, srsName, bbox);

    // Analysis the properties
    FdoStringP computedProperty;
    _analysisIdentifier (srsName, bbox, computedProperty, height, width);
    
    // Get the selected properties
    FdoPtr<FdoClassDefinition> classDefPruned = _getPrunedClassDefinition ();

    // Get MapType from connection
    FdoString* mapType = mConnection->GetMapType();
    FdoDouble scale;

    // Create a FeatureReader on the stream and return it to the user
    FdoPtr<FdoCMSFeatureReader> ret;
    FdoCMSRect rect(bbox->GetMinX(), bbox->GetMinY(), bbox->GetMaxX(), bbox->GetMaxY());

    GDALDataset* pGDALDataset = CMSDelegate->GetMapGdalDataset(bbox, height, width, mapType, scale);
    assert(pGDALDataset);
    ret = new FdoCMSFeatureReader (pGDALDataset, rect, featClass, classDefPruned, scale); 
    return (FDO_SAFE_ADDREF (ret.p));
}

/// <summary>Executes the select command and returns a reference to an
/// IFeatureReader.</summary> 
/// <returns>Returns the feature reader.</returns> 
FdoIFeatureReader* FdoCMSSelectCommand::ExecuteWithLock ()
{
    FdoPtr<FdoCMSFeatureReader> ret;

    ret = new FdoCMSFeatureReader ();

    return (FDO_SAFE_ADDREF (ret.p));
}

/// <summary> When executing the operation ExecuteWithLock lock 
/// conflicts may occur. Those lock conflicts are reported. The 
/// function GetLockConflicts returns a lock conflict reader 
/// providing access to the list of lock conflicts that occurred 
/// during the execution of the operation.</summary>
/// <returns>Returns a lock conflict reader.</returns> 
FdoILockConflictReader* FdoCMSSelectCommand::GetLockConflicts ()
{
    throw FdoCommandException::Create (NlsMsgGet (FDOCMS_LOCKING_NOT_SUPPORTED, "Locking not supported."));
}

FdoClassDefinition* FdoCMSSelectCommand::_getPrunedClassDefinition ()
{
    // Get the original class definition
    FdoPtr<FdoFeatureClass> clsDef;
    FdoStringP clsName;
    FdoStringP schemaName;

    FdoPtr<FdoIdentifier> identity = this->GetFeatureClassName ();
    clsName = identity->GetName ();
    schemaName = identity->GetSchemaName ();

    FdoFeatureSchemasP schemas = mConnection->GetSchemas ();

    if (schemaName == L"")
    {		
        FdoPtr<FdoIDisposableCollection> clsColl = schemas->FindClass (clsName);
        if (clsColl->GetCount () == 0)
            throw FdoSchemaException::Create (NlsMsgGet(FDOCMS_NAMED_SCHEMACLASS_NOT_FOUND, "FDO Feature Class '%1$ls' was not found.", (FdoString*)clsName ));
        
        clsDef = dynamic_cast<FdoFeatureClass *> (clsColl->GetItem(0));
    }
    else
    {
        FdoPtr<FdoFeatureSchema> schema = schemas->FindItem (schemaName);
        if (schema == NULL)
            throw FdoSchemaException::Create (NlsMsgGet(FDOCMS_NAMED_SCHEMA_NOT_FOUND, "FDO Schema '%1$ls' was not found.", (FdoString*)schemaName));

        FdoPtr<FdoClassCollection> clsColl = schema->GetClasses ();
        clsDef = dynamic_cast<FdoFeatureClass *> (clsColl->FindItem (clsName));
    }

    if (mPropertiesToSelect && mPropertiesToSelect->GetCount () == 0)
    {
        return FDO_SAFE_ADDREF (clsDef.p);
    }

    FdoPtr<FdoClassDefinition> ret = FdoCommonSchemaUtil::DeepCopyFdoFeatureClass (clsDef);
    
    FdoPtr<FdoPropertyDefinition> prop;
    FdoPtr<FdoPropertyDefinitionCollection> properties = ret->GetProperties ();
    FdoPtr<FdoDataPropertyDefinitionCollection> ids = ret->GetIdentityProperties();

    // find the raster property first in the properties
    FdoPtr<FdoRasterPropertyDefinition> rasterProp;
    bool bRaster = false;
    for (FdoInt32 i=0; i<properties->GetCount(); i++)
    {
        FdoPtr<FdoPropertyDefinition> prop = properties->GetItem (i);
        if (prop->GetPropertyType () == FdoPropertyType_RasterProperty)
        {
            // we found that raster property
            rasterProp = static_cast<FdoRasterPropertyDefinition *> (FDO_SAFE_ADDREF (prop.p));
            bRaster = true;
            break;
        }
    }

    // if the raster property is not in properties, search it in the base properties 
    if (!bRaster)
    {
        FdoPtr<FdoReadOnlyPropertyDefinitionCollection> baseProps = ret->GetBaseProperties ();
        for (FdoInt32 i=0; i<baseProps->GetCount(); i++)
        {
            FdoPtr<FdoPropertyDefinition> prop = baseProps->GetItem (i);
            if (prop->GetPropertyType () == FdoPropertyType_RasterProperty)
            {
                // we found that raster property
                rasterProp = static_cast<FdoRasterPropertyDefinition *> (FDO_SAFE_ADDREF (prop.p));
                bRaster = true;
                break;
            }
        }
    }

    // prune off the properties the caller hasn't provided in collection
    for (int j = properties->GetCount()-1; j >= 0; j--)
    {
        prop = properties->GetItem(j);
        bool bFound = false;

        for (int i = 0; i < mPropertiesToSelect->GetCount (); i++)
        {
            FdoPtr<FdoIdentifier> propValName = mPropertiesToSelect->GetItem (i);
            FdoString *propValNameString = propValName->GetName();

            if (0==wcscmp(prop->GetName(), propValNameString))
            {
                bFound = true; 
                break;
            }
        }

        // Remove this property, if not in propsToSelect
        if (!bFound)
        {
            // Remove from identity property collection, if necessary:
            if (prop->GetPropertyType() == FdoPropertyType_DataProperty)
            {
                FdoDataPropertyDefinition *dataProperty = static_cast<FdoDataPropertyDefinition*>(prop.p);
                if (ids->Contains(dataProperty))
                    ids->Remove(dataProperty);
            }

            // Remove from main property collection:
            properties->Remove(prop);
        }
    }

    // Add the computed properties
    for (FdoInt32 i = 0; i < mPropertiesToSelect->GetCount (); i++)
    {
        FdoPtr<FdoIdentifier> identifier = mPropertiesToSelect->GetItem (i);
        FdoComputedIdentifier* computedIdentifier = dynamic_cast<FdoComputedIdentifier*> (identifier.p);
        if (computedIdentifier)
        {
            FdoString* rasterPropName = computedIdentifier->GetName ();
            FdoPtr<FdoPropertyDefinition> prop = FdoCommonSchemaUtil::DeepCopyFdoPropertyDefinition (rasterProp);
            prop->SetName (rasterPropName);
            properties->Add (prop);
            ret->SetIsComputed (true);
        }
    }

    return FDO_SAFE_ADDREF (ret.p);
}

// Analysis the properties to check whether the RESAMPLE or CLIP function is invoked.
void FdoCMSSelectCommand::_analysisIdentifier (FdoString* srsName, FdoCMSBoundingBox* bbox, FdoStringP& propertyName, FdoSize& height, FdoSize& width)
{
    bool bResample = false;
    // bool bClip = false;

    FdoPtr<FdoIdentifierCollection> identifiers = GetPropertyNames ();

    for (FdoInt32 i=0; i<identifiers->GetCount (); i++)
    {
        FdoPtr<FdoIdentifier> identifier = identifiers->GetItem (i);
        FdoStringP propName = identifier->GetName ();
        FdoComputedIdentifier* computedIdentifier = dynamic_cast<FdoComputedIdentifier*> (identifier.p);

        if (computedIdentifier != NULL)
        {
            FdoPtr<FdoExpression> expr = computedIdentifier->GetExpression();
            FdoFunction* func = dynamic_cast<FdoFunction*> (expr.p);

            FdoCMSRect clippingBounds(0, 0, 0, 0);

            // CLIP() function can be nested
            while (true)
            {
                FdoString* funcName = func->GetName ();
                if (wcscmp (funcName, FdoCMSGlobals::ResampleFunctionName) == 0)
                    bResample = true;
                //else if (wcscmp (funcName, FdoCMSGlobals::ClipFunctionName) == 0)
                //	bClip = true;
                else
                    throw FdoCommandException::Create (NlsMsgGet (FDOCMS_ONLY_RESAMPLE_CLIP_FUNCTION_SUPPORTED, "Only RESAMPLE() function are supported.")); // TODO Remove CLIP

                FdoPtr<FdoExpressionCollection> args = func->GetArguments ();
                if (bResample && args->GetCount () != 7)
                    throw FdoCommandException::Create (NlsMsgGet (FDOCMS_RESAMPLE_FUNCTION_INCORRECTLY_USED, "RESAMPLE() function is incorrectly used."));
                //if (bClip && args->GetCount() != 5)
                //	throw FdoCommandException::Create (NlsMsgGet (FDOCMS_CLIP_FUNCTION_INCORRECTLY_USED, "CLIP() function is incorrectly used."));

                // Check the arguments types.
                FdoPtr<FdoExpression> arg0 = args->GetItem(0);

                FdoPtr<FdoExpression> arg = args->GetItem(1);
                FdoDataValue* minX = dynamic_cast<FdoDataValue*> (arg.p);
                arg = args->GetItem(2);
                FdoDataValue* minY = dynamic_cast<FdoDataValue*> (arg.p);
                arg = args->GetItem(3);
                FdoDataValue* maxX = dynamic_cast<FdoDataValue*> (arg.p);
                arg = args->GetItem(4);
                FdoDataValue* maxY = dynamic_cast<FdoDataValue*> (arg.p);

                FdoDataValue* heightArg = NULL;
                FdoDataValue* widthArg = NULL;

                if (bResample)
                {
                    arg = args->GetItem(5);
                    heightArg = dynamic_cast<FdoDataValue*> (arg.p);
                    arg = args->GetItem(6);
                    widthArg = dynamic_cast<FdoDataValue*> (arg.p);
                    if (minX == NULL || minY == NULL || maxX == NULL || maxY == NULL ||
                        heightArg == NULL || widthArg == NULL ||
                        (minX->GetDataType() != FdoDataType_Double && 
                        minX->GetDataType() != FdoDataType_Int16 &&
                        minX->GetDataType() != FdoDataType_Int32 && 
                        minX->GetDataType() != FdoDataType_Int64) ||
                        (minY->GetDataType() != FdoDataType_Double && 
                        minY->GetDataType() != FdoDataType_Int16 &&
                        minY->GetDataType() != FdoDataType_Int32 && 
                        minY->GetDataType() != FdoDataType_Int64)  ||
                        (maxX->GetDataType() != FdoDataType_Double && 
                        maxX->GetDataType() != FdoDataType_Int16 &&
                        maxX->GetDataType() != FdoDataType_Int32 && 
                        maxX->GetDataType() != FdoDataType_Int64)  ||
                        (maxY->GetDataType() != FdoDataType_Double && 
                        maxY->GetDataType() != FdoDataType_Int16 &&
                        maxY->GetDataType() != FdoDataType_Int32 && 
                        maxY->GetDataType() != FdoDataType_Int64) ||
                        (heightArg->GetDataType() != FdoDataType_Double && 
                        heightArg->GetDataType() != FdoDataType_Int16 &&
                        heightArg->GetDataType() != FdoDataType_Int32 && 
                        heightArg->GetDataType() != FdoDataType_Int64) ||
                        (widthArg->GetDataType() != FdoDataType_Double && 
                        widthArg->GetDataType() != FdoDataType_Int16 &&
                        widthArg->GetDataType() != FdoDataType_Int32 && 
                        widthArg->GetDataType() != FdoDataType_Int64) )
                        throw FdoCommandException::Create(NlsMsgGet(FDOCMS_RESAMPLE_FUNCTION_INCORRECTLY_USED, "RESAMPLE() function is incorrectly used."));
                }
                //else if (bClip)
                //{
                //	if (minX == NULL || minY == NULL || maxX == NULL || maxY == NULL ||					
                //		(minX->GetDataType() != FdoDataType_Double && 
                //		minX->GetDataType() != FdoDataType_Int16 &&
                //		minX->GetDataType() != FdoDataType_Int32 && 
                //		minX->GetDataType() != FdoDataType_Int64) ||
                //		(minY->GetDataType() != FdoDataType_Double && 
                //		minY->GetDataType() != FdoDataType_Int16 &&
                //		minY->GetDataType() != FdoDataType_Int32 && 
                //		minY->GetDataType() != FdoDataType_Int64)  ||
                //		(maxX->GetDataType() != FdoDataType_Double && 
                //		maxX->GetDataType() != FdoDataType_Int16 &&
                //		maxX->GetDataType() != FdoDataType_Int32 && 
                //		maxX->GetDataType() != FdoDataType_Int64)  ||
                //		(maxY->GetDataType() != FdoDataType_Double && 
                //		maxY->GetDataType() != FdoDataType_Int16 &&
                //		maxY->GetDataType() != FdoDataType_Int32 && 
                //		maxY->GetDataType() != FdoDataType_Int64) )
                //		throw FdoCommandException::Create(NlsMsgGet(FDOCMS_CLIP_FUNCTION_INCORRECTLY_USED, "CLIP() function is incorrectly used."));
                //}

                FdoCMSRect bounds(atof ((const char*)FdoStringP(minX->ToString ())),
                    atof ((const char*)FdoStringP(minY->ToString ())),
                    atof ((const char*)FdoStringP(maxX->ToString ())),
                    atof ((const char*)FdoStringP(maxY->ToString ())));

                if (clippingBounds.IsValid())
                    clippingBounds = clippingBounds.Intersect (bounds);
                else
                    clippingBounds = bounds;

                if (bResample)
                {
                    height = atoi ((const char*)FdoStringP(heightArg->ToString ()));
                    width = atoi ((const char*)FdoStringP(widthArg->ToString ()));
                }
                else
                {
                    height = width = 0;
                }

                // try to find the next nested function
                // first try function
                func = dynamic_cast<FdoFunction *> (arg0.p);
                if (func == NULL)
                {
                    // then try computed property
                    FdoComputedIdentifier* computedIdentifier = dynamic_cast<FdoComputedIdentifier *> (arg0.p);
                    if (computedIdentifier != NULL)
                    {
                        FdoPtr<FdoExpression> expr = computedIdentifier->GetExpression();
                        func = dynamic_cast<FdoFunction *> (expr.p);
                    }
                }

                // Now arg0 must be an identifier, otherwise there must be somthing wrong with user's input
                if (func == NULL)
                {
                    FdoIdentifier* id = dynamic_cast<FdoIdentifier *> (arg0.p);
                    if (id == NULL)
                    {
                        if (bResample)
                            throw FdoCommandException::Create(NlsMsgGet(FDOCMS_RESAMPLE_FUNCTION_INCORRECTLY_USED, "RESAMPLE() function is incorrectly used."));
                        //else if (bClip)
                        //	throw FdoCommandException::Create(NlsMsgGet(FDOCMS_CLIP_FUNCTION_INCORRECTLY_USED, "CLIP() function is incorrectly used."));
                    }
                    propertyName = id->GetText ();
                    break;
                }
            }

            bbox->SetMinX (clippingBounds.m_minX);
            bbox->SetMinY (clippingBounds.m_minY);
            bbox->SetMaxX (clippingBounds.m_maxX);
            bbox->SetMaxY (clippingBounds.m_maxY);

            // since we've got the computed property, stop the loop here
            break;
        }
    }

    if (//!bClip && 
        !bResample)
    {
        const FdoDouble DefaultMinX = -180.0;
        const FdoDouble DefaultMaxX = 180;
        const FdoDouble DefaultMinY = -90;  // to -90 ???
        const FdoDouble DefaultMaxY = 90;   // to 90 ???
        const int DEFAULTRESOLUTIONX = 512; // 512
        const int DEFAULTRESOLUTIONY = 512;

        height = DEFAULTRESOLUTIONX;
        width = DEFAULTRESOLUTIONY;
        // TODO, it may need adjust according to specific service

        bbox->SetMinX(DefaultMinX);
        bbox->SetMaxX(DefaultMaxX);
        bbox->SetMinY(DefaultMinY);
        bbox->SetMaxY(DefaultMaxY);
    }

#ifdef _WIN32
    wchar_t output[512] = {0};
    swprintf_s(output, L"boundingbox minx=%f, miny=%f, maxX=%f, maxY=%f, width=%d, height=%d \n", bbox->GetMinX(),
        bbox->GetMinY(), bbox->GetMaxX(), bbox->GetMaxY(), width, height);
    OutputDebugString(output);
#endif
}

FdoCMSDelegate* FdoCMSSelectCommand::_getCMSDelegate ()
{
    FdoPtr<FdoIConnectionInfo> info = mConnection->GetConnectionInfo ();
    FdoPtr<FdoIConnectionPropertyDictionary> dictionary = info->GetConnectionProperties ();

    FdoStringP location = dictionary->GetProperty (FdoCMSGlobals::ConnectionPropertyFeatureServer);
    if (0 == location.GetLength()) 
    {
        throw FdoConnectionException::Create (NlsMsgGet(FDOCMS_CONNECTION_REQUIRED_PROPERTY_NULL, 
            "The required connection property '%1$ls' cannot be set to NULL.", 
            FdoCMSGlobals::ConnectionPropertyFeatureServer));
    }

    FdoStringP key = dictionary->GetProperty (FdoCMSGlobals::ConnectionPropertyAPIKey);

    FdoPtr<FdoCMSDelegate> ret = FdoCMSDelegateFactory::Create(location, key);
    return FDO_SAFE_ADDREF (ret.p);
}

void FdoCMSSelectCommand::AdjustResolutionWithExtent(FdoSize& resolutionX, FdoSize& resolutionY, double dMinX, double dMinY, double dMaxX, double dMaxY)
{
    double deltaExtentsX = fabs(dMaxX - dMinX);
    double deltaExtentsY = fabs(dMaxY - dMinY);
 
    // Adjust resolution to the next power of 2
    FdoSize maxresolution = (((resolutionX) > (resolutionY)) ? (resolutionX) : (resolutionY));
    FdoSize power2resolution;
    for (power2resolution=1; power2resolution<MAXPOWER2RESOLUTION && power2resolution<maxresolution; power2resolution<<=1)
        ;
        
    if (deltaExtentsX > deltaExtentsY)
    {
        resolutionX = power2resolution;
        resolutionY = static_cast<int>(resolutionX * deltaExtentsY / deltaExtentsX);
    }
    else
    {
        resolutionY = power2resolution;
        resolutionX = static_cast<int>(resolutionY * deltaExtentsX / deltaExtentsY);
    }
}

void FdoCMSSelectCommand::SetFeatureClassName(FdoString *value)
{
    // mConnection->SetMapType(value);
    FdoCMSFeatureCommand<FdoISelect>::SetFeatureClassName(value);
}

